#include "../include/cat-data.hpp"
#include "../include/util/cmem.hpp"
#include "../include/util/platform.hpp"
#include <intrin.h>

static inline void cat_data_destroy(struct cat_data* data)
{
	data->json = NULL;
	bfree(data);
}

cat_data_t* cat_data_create()
{
	struct cat_data* data = (cat_data*)bzalloc(sizeof(struct cat_data));
	data->ref = 1;

	return data;
}

cat_data_t* cat_data_create_from_json(const char* json_string)
{
	cat_data_t* data = cat_data_create();

	JSONCPP_STRING error;
	Json::CharReaderBuilder json_builder;
	Json::Value root;
	Json::CharReader* json_reader = json_builder.newCharReader();
	bool parsingSuccessful = json_reader->parse(json_string, json_string + strlen(json_string), &root, &error);

	if (parsingSuccessful) {
		data->json = root;
	}
	else {
		blog(LOG_ERROR,
			"cat-data.cpp: [cat_data_create_from_json] "
			"Failed reading json string %s",
			error.c_str());
		cat_data_release(data);
		data = NULL;
	}
	delete json_reader;

	return data;
}

cat_data_t* cat_data_create_from_json_file(const char* json_file)
{
	char* file_data = LoadFileAsBytes(json_file);
	cat_data_t* data = NULL;

	if (file_data) {
		data = cat_data_create_from_json(file_data);
		bfree(file_data);
	}
	else {
		blog(LOG_WARNING,"cat-data.cpp: [cat_data_create_from_json_file] "
			"Failed opening file %s",json_file);
	}

	return data;
}

bool cat_data_write_json_file(cat_data_t* data, const char* json_file)
{
	return WriteJsonFile(data->json,json_file);
}

static inline bool update(Json::Value& cfg_default, Json::Value& cfg)
{
	bool is_update = true;
	for (const auto& key : cfg.getMemberNames())
		if (cfg_default.isMember(key))
		{
			if (cfg_default[key].type() != cfg[key].type())
			{
				blog(LOG_ERROR, "Value type error in config.json");
				return false;
			}
			if (cfg_default[key].isArray())
				for (Json::Value& v : cfg[key])
					if (v.type() != cfg_default[key][0].type())
					{
						if (v.isNull()) 
						{
							continue;
						}
						else
							blog(LOG_ERROR, "Value type error in config.json");
						return false;
					}
			if (cfg_default[key].isObject())
				is_update &= update(cfg_default[key], cfg[key]);
			else
				cfg_default[key] = cfg[key];
		}
	return is_update;
}

bool cat_data_update(cat_data_t& dst, cat_data_t& src)
{
	bool is_update = true;
	for (const auto& key : src.json.getMemberNames())
		if (dst.json.isMember(key))
		{
			if (dst.json[key].type() != src.json[key].type())
			{
				blog(LOG_ERROR,"Value type error in config.json");
				return false;
			}
			if (dst.json[key].isArray())
				for (Json::Value& v : src.json[key])
					if (v.type() != dst.json[key][0].type())
					{
						blog(LOG_ERROR, "Value type error in config.json");
						return false;
					}
			if (dst.json[key].isObject())
				is_update &= update(dst.json[key], src.json[key]);
			else
				dst.json[key] = src.json[key];
		}
	return is_update;
}

void cat_data_release(cat_data_t* data)
{
	if (!data)
		return;

	if (_InterlockedDecrement(&data->ref) == 0)
		cat_data_destroy(data);
}

int cat_data_get_mode(cat_data_t* data)
{
	if (!data)
		return -1;

	return data->json["plugins"]["mode"].asInt();
}


